﻿using System;
using System.Linq;
using System.Threading.Tasks;

using Hang.Blog.Dtos.Article;
using Hang.Blog.Entities;
using Hang.Blog.Helper;
using Hang.Blog.Inter;
using Hang.Blog.RepositoryInter;

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

using Volo.Abp.Domain.Repositories;

namespace Hang.Blog.Imp {
    [Route(StringConsts.Api + "article")]
    public class ArticleAppService : BlogAppService, IArticleAppService {

        private readonly IBlogRepository<Article> _articleRepository;
        private readonly IBlogRepository<ArticleTag> _articleTagRepository;
        private readonly IBlogRepository<ArticleAndTag> _articleAndTagRepository;

        public ArticleAppService(IBlogRepository<Article> articleRepository, IBlogRepository<ArticleTag> articleTagRepository, IBlogRepository<ArticleAndTag> articleAndTagRepository) {
            _articleRepository = articleRepository;
            _articleTagRepository = articleTagRepository;
            _articleAndTagRepository = articleAndTagRepository;
        }


        /// <summary>
        /// 添加或者修改文章
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        [Route("addorupdate")]
        [HttpPost]
        public async Task AddOrUpdate(ArticleDto dto) {
            if (dto.Id.HasValue) {
                var article = await _articleRepository.FirstOrDefaultAsync(x => x.Id == dto.Id);
                article.Title = dto.Title;
                article.Content = dto.Content;
                article.LastModificationTime = Clock.Now;
                article.LastModifierId = CurrentUser.Id;
                article.UpdateUserName = CurrentUser.UserName;
                article.CategoryId = dto.CategoryId;
                await _articleRepository.UpdateAsync(article);
                //处理Tag
                var exists = await (from at in (await _articleAndTagRepository.GetQueryableAsync()).Where(x => x.ArticleId == article.Id)
                                    join t in (await _articleTagRepository.GetQueryableAsync())
                                    on at.TagId equals t.Id
                                    select t).ToListAsync();
                var needDeletetags = exists.Select(x => x.TagName).Except(dto.Tags);
                var needDeleteEntites = exists.Where(x => needDeletetags.Contains(x.TagName));
                //先删除跟这个文章的关系
                var needDelete = await _articleAndTagRepository.GetListAsync(x => x.ArticleId == article.Id && needDeleteEntites.Select(t => t.Id).Contains(x.TagId));
                await _articleAndTagRepository.BulkDeleteAsync(needDelete);
                //校验这些跟其他文章还有没有关系
                var existsArticleAndTag = await _articleAndTagRepository.GetListAsync(x => needDeleteEntites.Select(t => t.Id).Contains(x.TagId));
                var tagIds = existsArticleAndTag.Select(x => x.TagId);
                var tags = needDeleteEntites.Where(x => !tagIds.Contains(x.Id));
                await _articleTagRepository.BulkDeleteAsync(tags);

                //需要添加的 
                var needAddTags = dto.Tags.Except(exists.Select(x => x.TagName));
                var needAddTagEntties = await _articleTagRepository.GetListAsync(x => needAddTags.Contains(x.TagName));
                var addTagAndArticle = needAddTagEntties.Select(x => new ArticleAndTag() { ArticleId = article.Id, TagId = x.Id });
                await _articleAndTagRepository.BulkInsertAsync(addTagAndArticle);
                var needDoubleInsert = needAddTags.Except(needAddTagEntties.Select(t => t.TagName));
                var articleTags = needDoubleInsert.Select(x => new ArticleTag(GuidGenerator.Create()) {

                    TagName = x,

                });
                await _articleTagRepository.BulkUpdateAsync(articleTags);
                var articleAndTags = articleTags.Select(x => new ArticleAndTag() {
                    ArticleId = article.Id,
                    TagId = x.Id
                });
                await _articleAndTagRepository.BulkInsertAsync(articleAndTags);

            } else {
                var article = new Article() {
                    CategoryId = dto.CategoryId,
                    Content = dto.Content,
                    CreatorId = CurrentUser.Id,
                    Title = dto.Title,
                    CreationTime = Clock.Now,

                };
                await _articleRepository.InsertAsync(article);
                var existsTags = await (await _articleTagRepository.GetQueryableAsync()).Where(x => dto.Tags.Contains(x.TagName)).ToListAsync();
                var notExists = dto.Tags.Except(existsTags.Select(x => x.TagName)).Select(x => new ArticleTag(GuidGenerator.Create()) {
                    TagName = x,

                });
                await _articleTagRepository.BulkInsertAsync(notExists.ToList());
                var relations = existsTags.Union(notExists).Select(x => new ArticleAndTag() {
                    ArticleId = article.Id,
                    TagId = article.Id,
                });
                await _articleAndTagRepository.BulkInsertAsync(relations);

            }
        }
    }
}
